/**
 * Copyright 2009 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.sapien.interceptor;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts2.StrutsStatics;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.ParameterList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

/**
 * Open ID interceptor.  Handles open id requests.
 * @author Adam
 * @version $Id: OpenIDInterceptor.java 30 2010-01-10 19:05:55Z a.ruggles $
 * 
 * Created on Apr 13, 2009 at 8:38:58 PM 
 */
public class OpenIDInterceptor extends AbstractInterceptor implements StrutsStatics {
	/**
	 * Serial Version UID.
	 */
	private static final long serialVersionUID = -8806896999350959556L;

	/**
     * The <code>Logger</code> is used by the application to generate a log messages.
     */
    private static final Logger LOG = LoggerFactory.getLogger(OpenIDInterceptor.class);

    /**
     * OpenID consumer Manager.
     */
    private final ConsumerManager manager;

	/**
     * Constructs a OpenID interceptor.
     * @param consumerManager The OpenID ConsumerManager.
     */
    @Inject
    public OpenIDInterceptor(final ConsumerManager consumerManager) {
    	super();
    	this.manager = consumerManager;
    }

	/**
	 * {@inheritDoc}
	 * @see com.opensymphony.xwork2.interceptor.AbstractInterceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public String intercept(final ActionInvocation invocation) throws Exception {
		final ActionContext context = invocation.getInvocationContext();
		final HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
		final HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
		final HttpSession session = request.getSession(true);
		String actionVal;
		// String serverContext = getServerContext(request);
		if ("/openid".equals(request.getServletPath())) {
			LOG.debug("I will process this request");
			// extract the parameters from the authentication response
		    // (which comes in as a HTTP request from the OpenID provider)
		    final ParameterList openidResp = new ParameterList(request.getParameterMap());

		    // retrieve the previously stored discovery information
		    final DiscoveryInformation discovered = (DiscoveryInformation) session.getAttribute("discovered");

		    // extract the receiving URL from the HTTP request
		    final StringBuffer receivingURL = request.getRequestURL();
		    final String queryString = request.getQueryString();
		    if (queryString != null && queryString.length() > 0) {
		        receivingURL.append('?').append(request.getQueryString());
		    }

		    // verify the response
		    final VerificationResult verification = manager.verify(receivingURL.toString(), openidResp, discovered);

		    // examine the verification result and extract the verified identifier
		    final Identifier verified = verification.getVerifiedId();

		    if (verified == null) {
		    	// OpenID authentication failed
		    	LOG.debug("I FAILED BADLY!!!!!!!!!");
		    } else {
		    	// success, use the verified identifier to identify the user
		    	LOG.debug("I WAS SUCCESSFULL!!!!!!!!! Identifier {}", verified.getIdentifier());
		    }
		    actionVal = Action.NONE;
		} else if ("/proc-openid".equals(request.getServletPath())) {
			final String returnUrl = getServerContext(request) + "/openid";
			LOG.debug("Using {} as the return URL.", returnUrl);
			// perform discovery on the user-supplied identifier
			// String google = "https://www.google.com/accounts/o8/id";
			final String yahoo = "http://yahoo.com";
			final List discoveries = manager.discover(yahoo);
			// attempt to associate with the OpenID provider
		    // and retrieve one service endpoint for authentication
			final DiscoveryInformation discovered = manager.associate(discoveries);
			// store the discovery information in the user's session for later use
		    // leave out for stateless operation / if there is no session
			session.setAttribute("discovered", discovered);
			// obtain a AuthRequest message to be sent to the OpenID provider
			final AuthRequest authReq = manager.authenticate(discovered, returnUrl);
			response.sendRedirect(authReq.getDestinationUrl(true));
			actionVal = Action.NONE;
		} else {
			actionVal = invocation.invoke();
		}
		return actionVal;
	}

	/**
	 * Returns the full server context.
	 * @param request The Http Servlet Request.
	 * @return The requested URL.
	 */
	private String getServerContext(final HttpServletRequest request) {
		// Get the base url.
        final StringBuilder serverPath = new StringBuilder();
        serverPath.append(request.getScheme() + "://");
        serverPath.append(request.getServerName());
        if (request.getServerPort() != 80) {
            serverPath.append(":" + request.getServerPort());
        }
        serverPath.append(request.getContextPath());
        return serverPath.toString();
	}
}
